package parttern.delegate;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**

 ### 简介

 > 动态代理，代理类的字节码不是程序运行前生成，而是在运行时在虚拟机中自动创建

 代理模式的定义：为其他对象提供一种代理以控制对这个对象的访问。在某些情况下，一个对象不适合或者不能直接引用另一个对象，而代理对象可以在客户端和目标对象之间起到中介的作用。

 著名的代理模式例子为引用计数（英语：reference counting）指针对象。

 当一个复杂对象的多份副本须存在时，代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者，每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时，复杂对象会被移除。

 ### 组成

 抽象角色：通过接口或抽象类声明真实角色实现的业务方法。

 代理角色：实现抽象角色，是真实角色的代理，通过真实角色的业务逻辑方法来实现抽象方法，并可以附加自己的操作。

 真实角色：实现抽象角色，定义真实角色所要实现的业务逻辑，供代理角色调用。

 ### 优点

 (1).职责清晰
 真实的角色就是实现实际的业务逻辑，不用关心其他非本职责的事务，通过后期的代理完成一件完成事务，附带的结果就是编程简洁清晰。

 (2).代理对象可以在客户端和目标对象之间起到中介的作用，这样起到了中介的作用和保护了目标对象的作用。

 (3).高扩展性

 ### 模式结构

 一个是真正的你要访问的对象(目标类)，一个是代理对象,真正对象与代理对象实现同一个接口,先访问代理类再访问真正要访问的对象。

 代理模式分为静态代理、动态代理。

 静态代理是由程序员创建或工具生成代理类的源码，再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件，代理类和委托类的关系在运行前就确定了。

 动态代理是在实现阶段不用关心代理类，而在运行阶段才指定哪一个对象。

 */
// 动态代理
public class DynamicAgent implements InvocationHandler {

    private Object mObject;    // 被代理的类引用

    public void setObject(Object object) {
        this.mObject = object;
    }

    public Object getObject() {
        return this.mObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("pre invoke...");
        Object object = method.invoke(mObject, args); // 调用被代理类的方法
        System.out.println("after invoke...");
        return object;
    }


    ///test
    public static void main(String[] args) {
        staticsTest(); // 代购帮买鞋和苹果并提供附加服务

        dynamicTest(); // 动态代理
    }

    private static void staticsTest() {
        // 想买鞋了
        IShoes nikeShop = new NikeShop();
        ProfessionalAgent nikeAgent = new ProfessionalAgent(nikeShop);
        nikeAgent.tryOn();
        nikeAgent.buyShoes();
        System.out.println();

        // 想买苹果了
        IApple appleStore = new AppleStore();
        ProfessionalAgent appleAgent = new ProfessionalAgent(appleStore);
        appleAgent.chose();
        appleAgent.buyIPhone();
        System.out.println();
    }

    private static void dynamicTest() {
        IShoes shoes = new NikeShop();
        DynamicAgent nikeAgent = new DynamicAgent();
        nikeAgent.setObject(shoes);
        // 动态构造代理鞋店
        IShoes shoesInstance = (IShoes) Proxy.newProxyInstance(shoes.getClass().getClassLoader(), shoes.getClass().getInterfaces(), nikeAgent);
        shoesInstance.tryOn();
        shoesInstance.buyShoes();
        System.out.println();
        IApple apple = new AppleStore();
        DynamicAgent appleAgent = new DynamicAgent();
        appleAgent.setObject(apple);
        IApple appleInstance = (IApple) Proxy.newProxyInstance(apple.getClass().getClassLoader(), apple.getClass().getInterfaces(), appleAgent);
        appleInstance.chose();
        appleInstance.buyIPhone();
    }
}